"""
Модуль для просмотра статистики чата.

- Таблица лидеров:
  * по сообщениям/словам
  * период до 300 дней (по умолчанию 7)
  * общее кол-во сообщений и слов
- Таблица лидеров самых НЕактивных, по времени последнего сообщения
- Динамика количества сообщения и слов за 30 дней:
  * для конкретного пользователя или всего Чаты
  * общие и среднии значения
- статистика участника:
  * статистика за период до 300 дней (по умолчанию 7)
  * указать участника или посмотреть на себя
  * статус святого +таймер до окончания статуса
  * находится ли пользователь в браке, с кем, как долго
  * кем был приглашён +скольких пользователей пригласил
  * как долго находится в чате +дата приглашения
  * когда было последнее сообщений, сколько времени прошло
  * Количество предупрждений +таймер до снятия следующиего
  * Количество жетонов +место в таблице лидеров, сколько/слов
- информация о беседе:
  * Сколько.. святых, запрешённх слов, предупреждений, не писавших, заблокированых, браков
  * Является ли чат зеркалом +список привязанных чатов
  * статистика жетонов: баланс, потеряно, выдано (минимум, максимум, среднее за месяц)

Author: Milinuri Nirvalen
"""

from chiori import Config
from chiori import Plugin
from libs.chat_control import count_time
from libs.chat_control import get_chat
from libs.chat_control import get_diff
from libs.chat_control import get_str_time
from libs.chat_control import tokens_log
from libs.utils import parse_timedelta
from libs.utils import sp
from libs.vk import args
from libs.vk import get_id
from libs.vk import get_profiles

import time


p = Plugin(name='чат: статистика чата', backend='vk',
	desc='модуль контроля чата, содержащий команды для просмотра статистики чата и его активности',
	args={
		'date':'время в фомате ЧИСЛО МНОЖИТЕЛЬ (минут, часов, дней, дней...)',
		'days':'кол-во дней (1-300); d(ay),,; f(ull)..',
		'target':args['target']
	})

# func
# ====

def count_tokens(c):
	'''посчитать кол-во токенов, которое будет сожжено
	:param c: экземпляр Config с данными 'tokens'

	:return: текст со статистикой'''

	tokens = sum(map(sum, c.values()))
	l = sum(map(lambda x: x[0], c.values()))

	return f'🔥 будет сожжено 🪙{l} жетонов (-{round(l/tokens*100, 2)}%❤)'


def get_mirrors(cid):
	'''получить список зеркал для опрделённой беседы
	:oaram cid: chat_id, id чата, для которого нужно получить список зеркал

	:return: список из id чатов-зеркал'''

	res = []
	c = Config(filepath='data/chats/mirror.toml').file_data
	for k, v in c.items():
		if v == cid:
			res.append(k)
	return res


def check_marriage(c, target):
	'''проверка на наличие брака
	:param c: экземпляр Config с данными чата
	:param target: id пользователя, для которого необходимо получить браки

	:return: возвращает данные брака или None'''

	for m in c['marriages']:
		if target in m['members']:
			return m


def convert_days(days=7):
	if not days:
		days = 7

	if isinstance(days, str) and days.isdigit():
		days = int(days)

	if days in ['full', 'f', 'всего', 'полное', 'global', 'полная', 'полный']:
		return 300
	elif days in ['day', 'd', 'день']:
		return 1
	elif isinstance(days, int):
		if days > 300:
			return 300
		else:
			return days
	else:
		return 300


def get_stats(c, key, days=7):
	days = convert_days(days)

	if str(key) not in c:
		return [0, 0]

	return [sum(map(lambda x: x[0], c[key][-days:])),
			sum(map(lambda x: x[1], c[key][-days:]))]


def count_all(c, days=7):
	days = convert_days(days)

	res = [0, 0]
	for k, v in c.items():
		data = get_stats(c, k, days)
		res[0] += data[0]
		res[1] += data[1]

	return res


def get_index(c, days=7):
	days = convert_days(days)

	profiles = {}
	for k, v in c.items():
		profiles[k] = get_stats(c, k, days)

	index = {}
	i = 0
	for k, v in sorted(profiles.items(), key=lambda x: x[1][1], reverse=True):
		i += 1
		index[i] = [k, v]
	return index


def get_index_place(c, key, days):
	i = 0
	for k, v in sorted(get_index(c, days).items()):
		if v[0] == key:
			i = k
			break

	if i == 1:
		return "🥇1"
	elif i == 2:
		return "🥈2"
	elif i == 3:
		return "🥉3"
	else:
		return str(i)


def messages_stats(c, days):
	days = convert_days(days)

	res = []
	for x in range(1, days+1):
		print(x)
		res.append([sum(map(lambda y: y[-x][0], c.values())),
			sum(map(lambda y: y[-x][1], c.values()))])

	return res[::-1]


def get_pr(index, key):
	return round(index.get(key, 0)/(sum(index.values()) or 1)*100, 2)

def get_tokens(index, target):
	if not target in index:
		return 0

	return sum(index[target])


# def get_message_rates(avarage):
# 	rates = []
# 	multiplies = [1, 2, 4, 8, 16]
# 	rates.append(int(avarage/2))
# 	for m in multiplies:
# 		rates.append(avarage*m)
# 	return rates


# def decor_rates(rates):
# 	i = 0
# 	text = ''
# 	for x in rates:
# 		i += 1

# 		if i in [2, 3]:
# 			text += f'┃ '
# 		else:
# 			text += f'│ '
# 		text += sp(str(x)) + ' '
# 	return text


# def get_to_level(rates, m):
# 	text = ''
# 	for x in filter(lambda x: x > m, rates):
# 		text += f'| {sp(str(x-m))} '
# 	return text


# def get_messages_stats(c, target):
# 	cm = c["messages"]
# 	cms = c["dayli_messages"]
# 	cw = c["words"]
# 	cws = c["dayli_words"]

# 	m = cm.get(target, 0)
# 	w = cw.get(target, 0)
# 	ms = cms.get(target, 0)
# 	ws = cws.get(target, 0)

# 	pro = get_pr(cm, target)
# 	pros = get_pr(cms, target)
# 	wpro = get_pr(cw, target)
# 	wpros = get_pr(cws, target)
# 	levels = get_levels(c, target)

# 	a = get_avarage(cm)
# 	sa = get_avarage(cms)
# 	aw = get_avarage(cw)
# 	aws = get_avarage(cws)

# 	rates = get_message_rates(a)
# 	s_rates = get_message_rates(sa)
# 	w_rates = get_message_rates(aw)
# 	sw_rates = get_message_rates(aws)


# 	text = f'''\n\n{"─"*5} < активность > {"─"*5}
# 🔘 всего / день:
# ✉ сообщения:
# -- {sp(m)} ({sp(w)}) ({pro}% / {wpro}%)
# -- {sp(ms)} ({sp(ws)}) ({pros}% / {wpros}%)

# 🌟 уровень:
# -- {levels["al"]} ({levels["m"]} / {levels["w"]}) уровень: {"⭐"*levels["al"]}
# -- {levels["als"]} ({levels["ms"]} / {levels["ws"]}) уровень: {"⭐"*levels["als"]}

# ♦ цели (сообщений / слов):
# -- {get_to_level(w_rates, w)}
# -- {get_to_level(sw_rates, ws)}
# -- {get_to_level(rates, m)}
# -- {get_to_level(s_rates, ms)}'''
# 	return text


# commands
# ========

@p.command('топ', 'top', 'rating',
	usage='[days/7] таблица лидеров по сообщениям и словам',
	check_func={'check_chat':0}, else_func='chioNotAdmin')
async def leaderbord(event, ctx):
	c = get_chat(event).file_data['counters']
	data = count_all(c)
	profiles = await get_profiles(event, ctx, c.keys())
	days = convert_days(ctx.sargs) or 7

	text = f'таблица лидеров по активности за {days} дней: (сообщений / слов)'

	index = get_index(c, days)

	for k, v in sorted(index.items()):
		if k == 31:
			break

		if int(v[0]) < 0:
			continue

		pr = profiles[v[0]]
		percent = round(v[1][1]/(data[1]or 1)*100, 2)

		if k == 1:
			k = "🥇"
		elif k == 2:
			k = "🥈"
		elif k == 3:
			k = "🥉"

		if v[1][0]:
			text += f'\n{k} [{percent}%] {pr["name"]}: {sp(v[1][0])} | {sp(v[1][1])}'


	am = sp(round(data[0]/days))
	aw = sp(round(data[1]/days))

	text += f'\n\n-- всего: {sp(data[0])} | {sp(data[1])}\n-- в среднем: {am} | {aw}'

	await ctx.message(text)


@p.command('stats', 'стат(ы/а/истика/)',
	usage='[target] [days/7] статистика пользователя в чате',
	check_func={'check_chat':0}, else_func='chioNotAdmin')
async def userStats(event, ctx):
	c = get_chat(event).file_data
	profiles = event.get('to.profiles')
	target, args = await get_id(event, ctx, ctx.args)
	if not target or target not in profiles:
		target = str(event.get('from.id'))

	pr = profiles[target]
	if target == str(event.get('from.id')):
		text = f'🧹 ваша статистика [{target}]'
	else:
		text = f'🧹 статистика [{target}] {pr["name"]}'


	# информация о святости
	# ====================

	if target in c['holy_members']:
		end_time = c["holy_members"][target] - ctx.now
		text += f'\n🔘 [{get_str_time(end_time)}] статус святого'


	# информация о браке
	# ==================

	m = check_marriage(c, target)
	if m:
		mid = ''
		removed = False
		for x in m["members"]:
			if x == target and not removed:
				removed = True
				continue
			else:
				mid = x
				break

		if mid in profiles:
			text += f'\n❤ в отношениях с {profiles[mid]["name"]}'
		else:
			text += f'\n❤ в отношениях с [id{mid}|{mid}]'

		t = count_time(ctx.now-m["date"])
		if t["d"]:
			text += f' ({get_str_time(t)})'


	# кем приглашён / сколько пригласил
	# =================================

	if not pr.get('invited_by'):
		text += f'\n📫 пользователь отцуцтвует'
	elif pr["invited_by"] > 0:
		if pr["invited_by"] in profiles:
			text += f'\n📫 приглашён {profiles[pr["invited_by"]]["name"]}'
		else:
			text += f'\n📫 приглашён [id{pr["invited_by"]}|{pr["invited_by"]}]'
	else:
		text += '\n📫 приглашён группой'

	i = 0
	for k, v in profiles.items():
		if str(v.get("invited_by", 0)) == target:
			i += 1
	if i > 0:
		text += f' (пригласил {i} чел)'


	# сколько находится в беседе
	# ==========================

	if pr.get('join_date'):
		jd = time.ctime(pr["join_date"])
		jd_dt = count_time(ctx.now-pr["join_date"])
		text += f'\n📅 с нами {get_str_time(jd_dt)} {f"(с {jd})" if jd_dt[0] else ""}'


	# информация о последнем сообщении
	# ================================

	lm = c['last_nessages'].get(target, [ctx.now, 1])[0]
	t_lm = time.ctime(lm)
	lm_dt = count_time(ctx.minutes-lm)
	text += f'\n⏳ последнее сообщение {get_str_time(lm_dt)} {f"({t_lm})" if lm_dt[0] else ""}'


	# предупреждения пользователю
	# ===========================

	if c['warns'].get(target):
		text += f'\n⚠️ предупреждений {len(c["warns"][target])} из 5'
		for k, v in sorted(c["warns"].get(target, {}).items()):
			text += f'\n-- [{get_str_time(int(k) - ctx.now)}]: {v}'


	# активность пользователя
	# =======================

	tokens = c["tokens"].	get(target, [0, 0, 0, 0, 0, 0, 0])
	tpro = round(sum(tokens)/(sum(map(sum, c['tokens'].values())) or 1)*100, 2)
	data = count_all(c['counters'], args)
	stats = get_stats(c['counters'], target, args)

	text += f'''\n🪙 жетоны: {sum(tokens)} ({get_diff(tokens[-2], tokens[-1])}; {tpro}%)
\n{get_index_place(c["counters"], target, args)} место в рейтинге
✉ [{round(stats[0]/(data[0] or 1)*100, 2)}%] {sp(stats[0])} ообщений
📃 [{round(stats[1]/(data[1] or 1)*100, 2)}%] {sp(stats[1])} слов
🪙 жетоны за неделю: {"; ".join(map(sp, tokens))}'''

	await ctx.message(text)


@p.command('неактив', 'inactive',
	usage='[date] список самых неактивных пользователей',
	check_func={'check_chat':0}, else_func='chioNotAdmin')
async def inactiveUsers(event, ctx):
	c = get_chat(event).file_data
	profiles = event.get('to.profiles')
	if ctx.sargs:
		date = ctx.now - parse_timedelta(ctx.sargs)
		text = f'неактив {ctx.sargs}:'

		for k, v in profiles.items():
			if k in c['last_nessages']:
				if c['last_nessages'][k][0] < date:

					text += f'\n-- {v["name"]}'
			else:
				text += f'\n-- 🐀{v["name"]}'

	else:
		text = f'самые неактивные участники: '

		cprofiles = await get_profiles(event, ctx,
                             c["last_nessages"].keys())

		rats = set(profiles.keys()) - set(cprofiles.keys())

		count = 0
		for k, v in sorted(c["last_nessages"].items(), key=lambda x: x[1]):
			count += 1

			pr = cprofiles[k]
			text += f'\n-- {pr["name"]} - {get_str_time(ctx.now-v[0])}'
			if count == 30:
				break

		if rats:
			text += f'\n\nне писали в беседу:'
		for x in rats:
			text += f'\n-- 🐀{profiles[x]["name"]}'

	await ctx.message(text)


@p.command('chat', 'чат', 'беседа', usage='информацию о чате',
	check_func={'check_chat':0}, else_func='chioNotAdmin')
async def chatStats(event, ctx):
	c = get_chat(event).file_data

	# актив в беседе
	h = tokens_log(c["tokens_history"])
	profiles = event.get('to.profiles')
	cprofiles = await get_profiles(event, ctx, c["last_nessages"].keys())
	rats = profiles.keys() - cprofiles.keys()
	mirrors = get_mirrors(c["cid"])
	data = count_all(c['counters'])

	tokens = sum(map(sum, c['tokens'].values())) or 1
	hp = round(tokens / (h["max_tokens"] or 1) *100, 2)

	text = f'{"🌐" if c["is_mirror"] else "🌍"} информацию о беседе:'

	if c["holy_members"]:
		text += f'\n🔘 святых: {len(c["holy_members"])}'

	if c["is_mirror"]:
		text += f'\n🔘 привязанный чат: {c["cid"]}'

	if len(c["words"]):
		text += f'\n🚫 запрещено слов: {len(c["words"])}'

	if sum(map(len, c["warns"].values())):
		text += f'\n⚠ всего предупрждений: {sum(map(len, c["warns"].values()))}'

	if len(c["banned"]):
		text += f'\n🔪 заблокировано пользователей: {len(c["banned"])}'

	if len(c["marriages"]):
		text += f'\n❤ браков в беседе: {len(c["marriages"])}'

	if len(rats):
		text += f'\n🐀 не писавшие в беседу: {len(rats)}'

	if len(mirrors):
		text += f'\n🌐 привязанные чаты: {len(mirrors)}'

	text += f'''\n\n✉ всего сообщений: {sp(data[0])}
📃 всего слов: {sp(data[1])}
🪙 [{hp}% ❤] всего жетонов: {tokens}
{count_tokens(c["tokens"])}
⏳ [{get_str_time(c["timers"]["day"] - ctx.now)}] обновления сезона

🔇 тихий режим: {"✅" if c["silent_mode"] else "❌"}
📎 id чата: {event.get("to.id")}'''

	for x in mirrors:
		text += f'\n-- {x}'

	text += f'''\n\n[ статистика жетонов ]
-- 🪙 баланс: {h["min_tokens"]} / {h["avarage_tokens"]} / {h["max_tokens"]}
-- 🔶 потеряно: {h["min_lose"]} / {h["avarage_lose"]} / {h["max_lose"]}
-- 🔷️ выдано: {h["min_added"]} / {h["avarage_added"]} / {h["max_added"]}'''

	await ctx.message(text)


@p.command('история-чата', 'chat-log',
	usage='[days/7] татистика сообщений за период',
	check_func={'check_chat':0}, else_func='chioNotAdmin')
async def chatLog(event, ctx):
	c = get_chat(event).file_data["counters"]
	days = convert_days(ctx.sargs)
	data = messages_stats(c, days)
	stats = count_all(c, days)
	max_words = max(map(lambda x: x[1], data))

	text = 'история активности в чате (сообщения | слова):'

	lm = 0
	lw = 0
	for x in data:
		hp = round(x[1] / max_words * 100, 2)
		status = ''

		if x[1] == max_words:
			status += '🔶'

		text += f'\n-- [{status}{hp}%; {get_diff(lm, x[0])}; {get_diff(lw, x[1])}] {sp(x[0])} | {sp(x[1])}'

		lm = x[0]
		lw = x[1]

	am = sp(round(stats[0]/days))
	aw = sp(round(stats[1]/days))

	text += f'\n\nвсего: {stats[0]} | {stats[1]}\nв среднем: {am} | {aw}'
	await ctx.message(text)


@p.command('расчёт', '<a(ctive) stat(s)>', '<а(актив) стат(а/ы/истика)>',
	usage='[target] [days/7] статистика активности участника',
	check_func={'check_chat':0}, else_func='chioNotAdmin')
async def countMessages(event, ctx):
	c = get_chat(event).file_data
	target, args = await get_id(event, ctx)
	profiles = event.get('to.profiles')
	if not target or target not in profiles:
		target = str(event.get('from.id'))

	days = convert_days(args)
	text = f'статистика {profiles[target]["name"]} по активности за {days} дней:'
	cc = c['counters'][target][300-days:]

	m = sum(map(lambda x: x[0], cc))
	w = sum(map(lambda x: x[1], cc))
	max_words = max(map(lambda x: x[1], cc))
	lm = 0
	lw = 0

	for x in cc:
		hp = round(x[1] / w * 100, 2)
		status = ''

		if x[1] == max_words:
			status += '🔶'

		text += f'\n-- [{status}{hp}%; {get_diff(lm, x[0])}; {get_diff(lw, x[1])}] {sp(x[0])} | {sp(x[1])}'

		lm = x[0]
		lw = x[1]

	am = sp(round(m/days))
	aw = sp(round(w/days))

	text += f'\n\nвсего: {m} | {w}\nв среднем: {am} | {aw}'

	await ctx.message(text)
